<?php

// $Id: DomTableExtractor.php 14 2011-11-05 13:05:56Z espendiller $
// $HeadURL: file:///var/svn-repos/phptools/trunk/DomTableExtractor/DomTableExtractor.php $

/**
 * This class converts a HTML table with headers or not to a php array which can also filter
 * It uses DOMDocument and XPath
 *
 */
class dom_table_extractor {

    private $headersToLower = false;
    private $combineHeaders = false;
    private $filterOutput = false;
    private $findHeaderOnTags = false;
    private $DeleteFirstRows = 0;

    public function __construct($headersToLower = false, $combineHeaders = false, $filterOutput = false) {
        $this->headersToLower = $headersToLower;
        $this->combineHeaders = $combineHeaders;
        $this->filterOutput = $filterOutput;
    }

    public function GetData($html, $selector) {

        // http://stackoverflow.com/questions/7429136/get-a-complete-table-with-php-domdocument-and-print-it

        $dom = $this->filter_dom_load($html);

        $xpath = new DOMXPath($dom);
        $table = $xpath->query('//table[' . $selector . ']')->item(0);

        // for printing the whole html table just type: print $xml->saveXML($table);

        $xpath = new DOMXPath($dom);


        $tableArray = array();
        foreach ($xpath->query('//table[' . $selector . ']//tr') as $row) {

            // header filter
            // $row -> getElementsByTagName('td');

            $tds = new DOMXPath($this->filter_dom_load($this->innerHTML($row)));
            $cells = $tds->query('//td|//th');

            $td = array();
            foreach ($cells as $cell) {
                // get raw data or text only
                $td[] = $this->filterOutput == true ? $this->innerHTML($cell) : $cell->NodeValue;
            }

            $tableArray[] = $td;
        }

        if ($this->DeleteFirstRows > 0) {
            $tableArray = array_slice($tableArray, $this->DeleteFirstRows);
        }

        if ($this->combineHeaders == true) {
            return $this->_CombineHeaders($tableArray);
        }

        return $tableArray;
    }

    private function _filterOutput($headerNames) {
        return trim(html_entity_decode(strip_tags($headerNames)));
    }

    private function _findHeaderOnTags($header, $unFilterdHeader) {
        if (strlen($header) == 0) {
            return preg_match('/(?:alt|title)="([^"]*)"/i', $unFilterdHeader, $match) ? $match[1] : 'empty';
        }

        return $header;
    }

    private function _CombineHeaders($tableArray) {

        // first row is header so remove it and get it
        $headerNames = array_shift($tableArray);

        if ($this->headersToLower == true) {
            $headerNames = array_map('strtolower', $headerNames);
        }

        // headers can contain html; filter it for headers / array keys
        if ($this->filterOutput == false) {
            $unFilterdHeader = $headerNames;
            $headerNames = array_map('self::_filterOutput', $headerNames);
        }

        if ($this->findHeaderOnTags == true) {
            $headerNames = array_map("self::_findHeaderOnTags", $headerNames, $unFilterdHeader);
        }

        // map key (headers) and value
        foreach ($tableArray as $rowData) {
            if (count($headerNames) == count($rowData)) {
                $data[] = array_combine($headerNames, $rowData);
            } else {
                // colspan or rowspan or invalid table!?
            }
        }

        return $data;
    }

    // http://api.drupal.org/api/drupal/modules--filter--filter.module/function/filter_dom_load/7
    private function filter_dom_load($text) {
        $dom_document = new DOMDocument();
        // Ignore warnings during HTML soup loading.
        @$dom_document->loadHTML('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /></head><body>' . $text . '</body></html>');

        return $dom_document;
    }

    // http://www.php.net/manual/en/book.dom.php#105815
    private function innerHTML($el) {
        $doc = new DOMDocument();
        $doc->appendChild($doc->importNode($el, TRUE));
        $html = trim($doc->saveHTML());
        $tag = $el->nodeName;
        return preg_replace('@^<' . $tag . '[^>]*>|</' . $tag . '>$@', '', $html);
    }

    public function HeadersToLower() {
        $this->headersToLower = true;
    }

    public function findHeaderOnTags() {
        $this->findHeaderOnTags = true;
    }

    public function CombineHeaders() {
        $this->combineHeaders = true;
    }

    public function DeleteFirstRows($numrows) {
        $this->DeleteFirstRows = $numrows;
    }

}
?>

